
/*
 * Copyright (C) 2008-2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id: MapFieldModel.java 295 2010-01-13 21:03:17Z archie.cobbs $
 */

package org.dellroad.sidekar.schema.model;

import java.lang.reflect.Method;

import org.dellroad.sidekar.schema.SchemaMismatchException;

/**
 * Schema type representing map types.
 *
 * @see org.dellroad.sidekar.SetField
 */
public class MapFieldModel extends ComplexFieldModel {

    public static final String NAME = "map";

    private SimpleFieldModel keyModel;
    private SimpleFieldModel valueModel;

    private Method accessMethod;

    // Normal constructor
    public MapFieldModel() {
        super(NAME);
    }

    // Copy constructor
    MapFieldModel(MapFieldModel original, SchemaModel schemaModel) {
        super(original);
        if (original.keyModel != null)
            this.keyModel = FieldDeepCopier.copy(original.keyModel, schemaModel);
        if (original.valueModel != null)
            this.valueModel = FieldDeepCopier.copy(original.valueModel, schemaModel);
        this.accessMethod = original.accessMethod;
    }

    /**
     * Get the type of map keys.
     */
    public SimpleFieldModel getKeyModel() {
        return this.keyModel;
    }
    public void setKeyModel(SimpleFieldModel keyModel) {
        this.keyModel = keyModel;
    }

    /**
     * Get the type of map values.
     */
    public SimpleFieldModel getValueModel() {
        return this.valueModel;
    }
    public void setValueModel(SimpleFieldModel valueModel) {
        this.valueModel = valueModel;
    }

    /**
     * Get the Java method used to access this field on the associated entity's class.
     * This property is optional.
     */
    public Method getAccessMethod() {
        return this.accessMethod;
    }
    public void setAccessMethod(Method accessMethod) {
        this.accessMethod = accessMethod;
    }

    @Override
    public void validate(SchemaModel schema) {
        super.validate(schema);
        this.keyModel.validate(schema, true);
        this.valueModel.validate(schema, true);
    }

    @Override
    public void checkSameField(FieldModel model) {
        super.checkSameField(model);
        MapFieldModel that = (MapFieldModel)model;
        try {
            this.keyModel.checkSameField(that.keyModel);
        } catch (SchemaMismatchException e) {
            throw new SchemaMismatchException("key types differ: " + e.getMessage(), e);
        }
        try {
            this.valueModel.checkSameField(that.valueModel);
        } catch (SchemaMismatchException e) {
            throw new SchemaMismatchException("value types differ: " + e.getMessage(), e);
        }
    }

    @Override
    public <R> R visit(FieldModelSwitch<R> sw) {
        return sw.caseMapFieldModel(this);
    }

    @Override
    public final String getTypeDescription() {
        return "map of " + this.keyModel.getTypeDescription() + " -> " + this.valueModel.getTypeDescription();
    }
}

